// --*- C++ -*------x-----------------------------------------------------------
//
// Class:           Align
//
// Author:          Enrico Negri, Alessandra Mantovan, Silvio Tosatto, Layla Hirsh
//
// Project name:    BiocompUP Align
//
// Date:            12/2007
//
// Description:     Pairwise sequence and profile alignment 
//
// -----------------x-----------------------------------------------------------

#include <Align.h>

namespace Biopool
{

// CONSTRUCTORS:

Align::Align(AlignmentData *ad, GapFunction *gf, ScoringScheme *ss) : ad(ad),
	gf(gf), ss(ss), F((ad->getSequence(1)).size() + 1),
		B((ad->getSequence(1)).size() + 1), n((ad->getSequence(1)).size()),
			m((ad->getSequence(2)).size()), res1Pos(), res2Pos()
{
	vector<double> frow(m+1, 0);
	vector<Traceback> brow(m+1);
	for (unsigned int i = 0; i < F.size(); ++i)
	{
		F[i] = frow;
		B[i] = brow;
	}

	setPenalties(0.98, 0.00);
}


Align::Align(const Align &orig)
{
	copy(orig);
}


Align::~Align()
{ }


// OPERATORS:

Align&
Align::operator = (const Align &orig)
{
	if (&orig != this)
		copy(orig);
	return *this;
}


// PREDICATES:

vector<double>
Align::getMultiMatchScore(unsigned int num)
{
	vector<double> score;

	for (unsigned int i = 0; i < num; i++)
	{
		score.push_back(getScore());
		getMultiMatch();
	}

	return score;
}


vector<string>
Align::getMatch() const
{
	string res1, res2;
	res1Pos.clear();
	res2Pos.clear();
	Traceback tb = B0;
	int i = tb.i;
	int j = tb.j;

	tb = next(tb);
	while (((tb.i >= 0) || (tb.j >= 0)) && ((i != tb.i) || (j != tb.j)))
	{
		if (i == tb.i)
		{
			res1 = res1 + "-";
			res1Pos.push_back(INVALID_POS);
		}
		else
		{
			res1 = res1 + (ad->getSequence(1))[i-1];
			res1Pos.push_back(i-1);
		}

		if (j == tb.j)
		{
			res2 = res2 + "-";
			res2Pos.push_back(INVALID_POS);
		}
		else
		{
			res2 = res2 + (ad->getSequence(2))[j-1];
			res2Pos.push_back(j-1);
		}

		ad->calculateMatch(i, tb.i, j, tb.j);

		i = tb.i;
		j = tb.j;
		tb = next(tb);
	}

	reverse(res1.begin(), res1.end());
	reverse(res2.begin(), res2.end());
	reverse(res1Pos.begin(), res1Pos.end());
	reverse(res2Pos.begin(), res2Pos.end());
	vector<string> res(2);
	res[0] = res1;
	res[1] = res2;
	return res;
}


vector< vector<int> >
Align::getMatchSubset()
{
	getMatch(); 
	vector< vector<int> > resultVecs(2);
	resultVecs[0] = res1Pos;
	resultVecs[1] = res2Pos;
	return resultVecs;
}


vector<int>
Align::shiftMatchSubset(vector<int> inputVector, int newStartPos)
{
	unsigned int len = inputVector.size();
	vector<int> newInputVector;
	int gapCount = 0;

	for (unsigned int i = 0 ; i < len ; i++)
		if (inputVector[i] == -1)
		{
			int gap = -1;
			gapCount++;
			newInputVector.push_back(gap);
		}
		else
		{
			int valueCurrent = inputVector[i];
			int newValue = valueCurrent + newStartPos;
			newInputVector.push_back(newValue);
		}

	return newInputVector;
}


void
Align::outputMultiMatch(ostream &os, unsigned int num, bool fasta)
{
	if (!fasta)
	{
		os << "Alignments:\n\n"
		   << "--xx--xx--xx--xx--xx--xx--xx--xx--xx--xx--xx--xx--xx--xx--xx--xx--xx--\n"
		   << endl;
	}

	for (unsigned int i = 0; i < num; i++)
	{
		if (!fasta)
			os << "Score = " << getScore() << "\t\t";
		getMultiMatch();
		ad->outputMatch(cout, fasta);
	}
}


vector<Alignment>
Align::generateMultiMatch(unsigned int num)
{
	vector<Alignment> va;

	for (unsigned int i = 0; i < num; i++)
	{
		double tmpScore = this->getScore();
		Alignment *tmp = new Alignment;
		getMultiMatch();
		*tmp = ad->generateMatch(tmpScore); 
		va.push_back(*tmp);
	}

	return va;
}


vector<double>
Align::generateMultiMatchScore(unsigned int num)
{
	vector<double> score;

	for (unsigned int i = 0; i < num; i++)
	{
		score.push_back(getScore());
		getMultiMatch();
	}

	return score;
}


void
Align::doMatchScore(ostream &os) const
{
	vector<string> match = getMatch();

	os << "Score = " << getScore() << "\n" << endl;
}

void
Align::doMatch(ostream &os) const
{
	vector<string> match = getMatch();

	os << match[0] << "\n"
	   << match[1] << "\n"
	   << endl;
}

void
Align::doMatchPlusHeader(ostream &os, string headerTarget,
	string headerTemplate) const
{
	vector<string> match = getMatch();

	os << "> " << headerTarget   << "\n"
	   << match[0] << "\n\n"
	   << "> " << headerTemplate << "\n"
	   << match[1] << "\n\n"
	   << "Score = " << getScore() << "\n" << endl;
}


// MODIFIERS:

void
Align::copy(const Align &orig)
{
	ad = orig.ad->newCopy();
	gf = orig.gf->newCopy();
	ss = orig.ss->newCopy();

	F.clear();
	for (unsigned int i = 0; i < orig.F.size(); i++)
	{
		vector<double> *tmp = new vector<double>;
		for (unsigned int j = 0; j < orig.F[i].size(); j++)
			tmp->push_back(orig.F[i][j]);
		F.push_back(*tmp);
	}

	B.clear();
	for (unsigned int i = 0; i < orig.B.size(); i++)
	{
		vector<Traceback> *tmp = new vector<Traceback>;
		for (unsigned int j = 0; j < orig.B[i].size(); j++)
			tmp->push_back(orig.B[i][j]);
		B.push_back(*tmp);
	}

	B0 = orig.B0;
	n = orig.n;
	m = orig.m;

	res1Pos.clear();
	for (unsigned int i = 0; i < orig.res1Pos.size(); i++)
		res1Pos.push_back(orig.res1Pos[i]);

	res2Pos.clear();
	for (unsigned int i = 0; i < orig.res2Pos.size(); i++)
		res2Pos.push_back(orig.res2Pos[i]);

	penaltyMul = orig.penaltyMul;
	penaltyAdd = orig.penaltyAdd;
}


void
Align::recalculateMatrix()
{
	for (unsigned int i = 0; i < F.size(); i++)
		for (unsigned int j = 0; j < F[i].size(); j++)
			F[i][j] = -999;

	for (unsigned int i = 0; i < B.size(); i++)
		for (unsigned int j = 0; j < B[i].size(); j++)
		{
			B[i][j].i = -999;
			B[i][j].j = -999;
		}

	B0.i = -999;
	B0.j = -999;

	res1Pos.clear();
	res2Pos.clear();

	pCalculateMatrix(true);
}

} // namespace
